Using JavaScript interfaces in WebViews to expose Java objects is unsafe. Doing so allows JavaScript to invoke Java methods, potentially giving
attackers access to data or sensitive app functionality. WebViews might include untrusted sources such as third-party iframes, making this
functionality particularly risky. As JavaScript interfaces are passed to every frame in the WebView, those iframes are also able to access the exposed
Java object.
Ask Yourself Whether
- The content in the WebView is fully trusted and secure.
- Potentially untrusted iframes could be loaded in the WebView.
- The JavaScript interface has to be exposed for the entire lifecycle of the WebView.
- The exposed Java object might be called by untrusted sources.
There is a risk if you answered yes to any of these questions.
Recommended Secure Coding Practices
Disable JavaScript
If it is possible to disable JavaScript in the WebView, this is the most secure option.
When using the webview_flutter
package, JavaScript is disabled by default.
Therefore, setJavaScriptMode
does not need to be explicitly called with JavaScriptMode.disabled
on WebViewController
(v4 and
above) or WebView
(v3 and
below).
When using the flutter_inappwebview
package, on the other hand, the
default behavior is to enable JavaScript. Therefore, it is necessary to set the enableJavaScript
parameter to false
in
instances of InAppWebViewSettings and InAppWebViewOptions.
Of course, sometimes it is necessary to enable JavaScript, in which case the following recommendations should be considered.
Remove JavaScript interface when loading untrusted content
JavaScript interfaces can be removed at a later point. It is recommended to remove the JavaScript interface when it is no longer needed. If it is
needed for a longer time, consider removing it before loading untrusted content. This can be done by calling
webViewController.removeJavaScriptChannel('channelName')
for the webview_flutter
package or
webViewController.removeJavaScriptHandler('channelName')
for the flutter_inappwebview
package.
A good place to do this in Flutter is inside navigation callbacks like NavigationDelegate.onNavigationRequest
for the webview_flutter
package or WebViewClient.shouldOverrideUrlLoading
for the flutter_inappwebview
package package, where you can check the URL
being loaded and remove the JavaScript channel if the content is untrusted.
Alternative methods to implement native bridges
If a native channel has to be added to the WebView or its controller, and it is impossible to remove it at a later point, consider using an
alternative method that offers more control over the communication flow.
For the webview_flutter
package, there is currently no way to know the origin of an incoming JavaScript message. Therefore using
JavaScript communication with webview_flutter
is discouraged.
For the flutter_inappwebview
package, use WebMessageListener
which allows you to restrict the origins that can send messages to your application.
Sensitive Code Example
webview_flutter
final _controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..addJavaScriptChannel("channel", onMessageReceived: (m) {}); // Sensitive
flutter_inappwebview
InAppWebView(
onWebViewCreated: (controller) {
controller?.addJavaScriptHandler( // Sensitive
handlerName: "channel",
callback: (args) {}
);
},
);
Compliant Solution
The most secure option is to not enable, or disable, JavaScript entirely. S6362 further explains why it should not be enabled unless
absolutely necessary.
webview_flutter
final _controller = WebViewController();
If possible, remove the JavaScript channel after it is no longer needed, or before loading any untrusted content.
webview_flutter
_controller.removeJavaScriptChannel("channel");
flutter_inappwebview
_controller.removeJavaScriptHandler("channel");
If a JavaScript channel must be used, consider using flutter_inappwebview
instead of webview_flutter
. The Web Message API
allows you to restrict the origins that can send messages to the JavaScript bridge.
InAppWebView(
onWebViewCreated: (controller) async {
await inAppController.addWebMessageListener(
WebMessageListener(
jsObjectName: "channel",
allowedOriginRules: {
"https://secure-origin",
}, // Restrict to specific origin
onPostMessage: (message, origin, isMainFrame, replyProxy) {},
),
);
},
);
See
Related rules
- S6362 - Enabling JavaScript support for WebViews is security-sensitive